
/*
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.

    To request the provided software under a different license you can contact us at
    support@xoronos.com
*/

#include "xrn_arithmetic.h"
#include "xrn_arithmetic_backend.h"

int xrn_check_sparsity( xnum_t num
 )
{
    XPRINT_DEB(  );

    int i, cnt;

    cnt = 0;
    for ( i = 0; i < XNBITS; i++ ) {
        cnt = cnt + ( int ) ( BIT_GET( num, i ) );
    }

    if ( ( cnt < 33 ) && ( XNBITS == 256 ) ) {
        return XERR_SPARSE_MKEY;
    } else if ( ( cnt < 19 ) && ( XNBITS == 128 ) ) {
        return XERR_SPARSE_MKEY;
    }

    return XSUCCESS;

}

int xrn_initialize_true_rnd_num( char *rnd_input, xnum_t * res
 )
{
    XPRINT_DEB(  );

    int len;
    int rndin;
    uint8_t rnd[XNBYTES];
    uint8_t buff[XNBYTES];
    uint32_t crc;
    xnum_t num;
    int i;

    rndin = 1;

    len = ( int ) strnlen( rnd_input, XRN_BUFF_SIZE );

    if ( ( rnd_input == NULL ) || ( len <= 0 ) ) {
        rndin = 0;
        rnd_input = ( char * ) malloc( sizeof( char ) * 8 );
        if ( rnd_input == NULL ) {
            XPRINT_ERR( 1995, XERR_NULL_MSG );
            return XERR_NULL;
        }

        strcpy( rnd_input, "xoronos" );
        len = ( int ) strnlen( rnd_input, XRN_MAX_STR_SIZE );
    }

    crc = 0xffffffff;
    for ( i = 0; ( i < sizeof( xnum_t ) ); i++ ) {
        xrn_crc32( ( uint8_t * ) & rnd_input[i % len], 1, &crc );
        buff[i] = ( uint8_t ) ( crc & 0x000000ff );
    }

    if ( get_random_bytes( rnd, sizeof( xnum_t ) ) != 0 ) {
        XPRINT_ERR( 1996, XERR_IO_MSG );
        if ( rndin == 0 ) {
            XRN_FREEN( rnd_input, len );
        }

        return XERR_IO;
    }

    // xor random bytes and random input
    for ( i = 0; i < sizeof( xnum_t ); i++ ) {
        buff[i] = rnd[i] ^ buff[i];
        ( ( uint8_t * ) & num )[i] = buff[i];
    }

    *res = num;
    if ( !rndin ) {
        XRN_FREEN( rnd_input, len );
    }

    return XSUCCESS;

}

void xrn_initialize_num( xnum_t * dst
 )
{
    XPRINT_DEB(  );

    for ( int i = 0; i < sizeof( xnum_t ); i++ ) {
        ( ( uint8_t * ) dst )[i] = 0;
    }

    return;

}

void xrn_initialize_mat( xmat_t * dst
 )
{
    XPRINT_DEB(  );

    for ( int i = 0; i < sizeof( xmat_t ); i++ ) {
        ( ( uint8_t * ) dst )[i] = 0;
    }

    return;

}

void xrn_initialize_rnd_num( xnum_t * dst
 )
{
    XPRINT_DEB(  );

    XBIT_TYPE bit;
    xnum_t tmp;
    int i;

    xrn_initialize_num( &tmp );

    for ( i = 0; i < sizeof( xnum_t ) * 8; i++ ) {
        if ( ( ( ( double ) rand(  ) ) / ( ( double ) RAND_MAX ) ) > 0.5 ) {
            bit = 1;
        } else {
            bit = 0;
        }

        BIT_SETVAL( tmp, i, bit );
    }

    *dst = tmp;
}

void xrn_initialize_rnd_mat( xmat_t * dst
 )
{
    XPRINT_DEB(  );

    for ( int i = 0; i < sizeof( xnum_t ) * 8; i++ ) {
        xrn_initialize_rnd_num( &dst->xmat[i] );
    }

}

void xrn_bin_to_hex( xnum_t xnum_in, char *str_out
 )
{
    XPRINT_DEB(  );

    int i;

    char str[128];

    strncpy( str_out, "", XRN_MAX_STR_SIZE );

    for ( i = 0; i < XNBITS_NUM; i++ ) {
#if   ( XARC64BIT )
#if ( XRN_GNU_LINUX == 1 )
        snprintf( str, 128, "%016lx", xnum_in.xnum[i] );
#else
        snprintf( str, 128, "%016llx", xnum_in.xnum[i] );
#endif
#elif ( XARC32BIT )
        snprintf( str, 128, "%08x", xnum_in.xnum[i] );
#elif ( XARC16BIT )
        snprintf( str, 128, "%04hx", xnum_in.xnum[i] );
#elif ( XARC08BIT )
        snprintf( str, 128, "%02hhx", xnum_in.xnum[i] );
#endif
        strncat( str_out, str, XRN_MAX_STR_SIZE );

    }

    return;

}

void xrn_hex_to_bin( char *str_in, xnum_t * xnum_out
 )
{
    XPRINT_DEB(  );

    int i;

    for ( i = 0; i < XNBITS_NUM; i++ ) {

#if   ( XARC64BIT )
#if ( XRN_GNU_LINUX == 1 )
        sscanf( str_in + i * 16, "%016lx", &xnum_out->xnum[i] );
#else
        sscanf( str_in + i * 16, "%016llx", &xnum_out->xnum[i] );
#endif
#elif ( XARC32BIT )
        sscanf( str_in + i * 8, "%08x", &xnum_out->xnum[i] );
#elif ( XARC16BIT )
        sscanf( str_in + i * 4, "%04hx", &xnum_out->xnum[i] );
#elif ( XARC08BIT )
        sscanf( str_in + i * 2, "%02hhx", &xnum_out->xnum[i] );
#endif

    }

    return;
}

void xrn_bin_to_base64( xnum_t xnum_in, char *str_out
 )
{
    XPRINT_DEB(  );

    base64_encode( ( char * ) &xnum_in, sizeof( xnum_t ), str_out );

#if XRUNCONFGEN == 1
    prettify_b64( str_out );
#endif

    return;

}

int xrn_base64_to_bin( char *str_in, xnum_t * xnum_out
 )
{
    XPRINT_DEB(  );

    int i;
    char buffer[XNBITS + 1];

    // remove "\"" character in string
    for ( i = 0; i < sizeof( xnum_t ) * 8; i++ ) {
        if ( str_in[i] == '"' ) {
            buffer[i] = '\0';
        } else {
            buffer[i] = str_in[i];
        }

    }

    buffer[XNBITS] = '\0';

#if XRUNCONFGEN == 1
    deprettify_b64( buffer );
#endif

#if ( (XNBITS + 1) > XRN_MAX_STR_SIZE )
    if ( base64_decode( buffer, ( int ) strnlen( buffer, XRN_MAX_STR_SIZE ), ( char * ) xnum_out ) !=
         XSUCCESS ) {
#else
    if ( base64_decode( buffer, ( int ) strnlen( buffer, ( XNBITS + 1 ) ), ( char * ) xnum_out ) != XSUCCESS ) {
#endif
        XPRINT_WAR( XWAR_FORMAT_MSG, XWAR_FORMAT );
        return ( int ) XWAR_FORMAT;
    }

    return XSUCCESS;
}

int xrn_add_nums( xnum_t a, xnum_t b, int carry, xnum_t * c
 )
{
    XPRINT_DEB(  );

    xnum_t tmp;
    int i, index;
    int permutations[XNBITS] = XRN_PERMUTATIONS;
    xnum_t perma, permb, permtmp;
    XBIT_TYPE bit;

    xrn_initialize_num( &perma );
    xrn_initialize_num( &permb );

    // change representation of the input
    for ( i = 0; i < sizeof( xnum_t ) * 8; i++ ) {
        index = permutations[i];

        bit = BIT_GET( a, i );
        BIT_SETVAL( perma, index, bit );

        bit = BIT_GET( b, i );
        BIT_SETVAL( permb, index, bit );
    }

    // compute the sum
    for ( i = 0; i < XNBITS_NUM; i++ ) {
        tmp.xnum[i] = ( perma.xnum[i] + permb.xnum[i] + ( uint64_t ) carry );
        carry = ( tmp.xnum[i] <= perma.xnum[i] ) * ( tmp.xnum[i] <= permb.xnum[i] );
    }

    // change representation of the output
    for ( i = 0; i < sizeof( xnum_t ) * 8; i++ ) {
        index = permutations[i];

        bit = BIT_GET( tmp, index );
        BIT_SETVAL( permtmp, i, bit );

    }

    *c = permtmp;

    return carry;
}

int xrn_subtract_nums( xnum_t a, xnum_t b, xnum_t * c
 )
{
    XPRINT_DEB(  );

    // perform the 2's complement for b
    X_INV2( b, b );
    return xrn_add_nums( a, b, 1, c );

}

void xrn_compare_greater_equal_nums( xnum_t a, xnum_t b, int *res
 )
{
    XPRINT_DEB(  );

    *res = 0;
    int i, index;
    int permutations[XNBITS] = XRN_PERMUTATIONS;
    xnum_t perma, permb;
    XBIT_TYPE bit;

    xrn_initialize_num( &perma );
    xrn_initialize_num( &permb );

    // change representation of the input
    for ( i = 0; i < sizeof( xnum_t ) * 8; i++ ) {
        index = permutations[i];

        bit = BIT_GET( a, i );
        BIT_SETVAL( perma, index, bit );

        bit = BIT_GET( b, i );
        BIT_SETVAL( permb, index, bit );
    }

    for ( int lu = XNBITS_NUM - 1;
          ( lu >= 0 ) && ( ( lu == XNBITS_NUM - 1 ) || ( perma.xnum[lu] == permb.xnum[lu] ) ); lu-- )
        *res = perma.xnum[lu] > permb.xnum[lu];

    return;

}

